#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/fb.h>
#include <jpeglib.h>
#include <time.h>
#include <pthread.h>
#include <signal.h>
#include <sys/time.h>
#include "lcd.h"
#include "../FIFO/fifo.h"
#include "font.h"

static inline uint16_t RGB888_to_RGB565(uint8_t r, uint8_t g, uint8_t b) {
    return ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
}

static struct jpeg_msg jpeg_fileA = {NULL,0,0};         // 初始化图片A的信息
static struct jpeg_msg jpeg_fileB = {NULL,0,0};         // 初始化图片B的信息
static struct jpeg_msg jpeg_fileC = {NULL,0,0};         // 初始化图片B的信息

static  struct lcd_info lcd;                            // 保存lcd的参数,感觉这个结构体写的不好


static void * cal_buffer;                               // 存储转换格式的信息

extern struct kfifo *global_carama_fifo;             // 存放carama的每帧的数据
int lcd_init(const char * lcd_path)
{

    static  struct fb_fix_screeninfo fb_fix;
    static  struct fb_var_screeninfo fb_var;
    /* 打开framebuffer设备 */
    if (0 > (lcd.lcd_fd = open(lcd_path, O_RDWR))) {
            perror("open lcd error");
            return -1;
    }
    if (pthread_mutex_init(&lcd.lock, NULL) != 0) {
        perror("pthread_mutex_init error");
        close(lcd.lcd_fd);
        return -1;
    }
    printf("准备获取参数\r\n");
    /* 获取参数信息 */
    ioctl(lcd.lcd_fd, FBIOGET_VSCREENINFO, &fb_var);
    ioctl(lcd.lcd_fd, FBIOGET_FSCREENINFO, &fb_fix);
    lcd.screen_size = fb_fix.line_length * fb_var.yres;
    lcd.width = fb_var.xres;
    lcd.height = fb_var.yres;
    lcd.screen_base = mmap(NULL, lcd.screen_size,  PROT_READ | PROT_WRITE, MAP_SHARED, lcd.lcd_fd, 0);
    lcd.CacheBuffer = (unsigned char * )malloc(lcd.screen_size);        // 只分配一块就行
    lcd.current_frame = 0;                                         // 当前用的哪一块
    lcd.bpp = fb_var.bits_per_pixel;
    lcd.current_text_row = 0;                                      // 从哪一行开始写

    if (MAP_FAILED == (void *)lcd.screen_base || lcd.CacheBuffer == NULL) {
        perror("mmap error");
        close(lcd.lcd_fd);
        pthread_mutex_destroy(&lcd.lock);
        if(lcd.CacheBuffer) {
            free(lcd.CacheBuffer);
            lcd.CacheBuffer = NULL;
        }
        return -1;
    }
    memset(lcd.CacheBuffer,0xff,lcd.screen_size);
    printf("lcd初始化成功\r\n");
    return 0;
}

void lcd_exit()
{
    if (lcd.screen_base && lcd.screen_base != MAP_FAILED) {
        munmap(lcd.screen_base, lcd.screen_size);
        lcd.screen_base = NULL;
    }

    if (lcd.lcd_fd >= 0) {
        close(lcd.lcd_fd);
        lcd.lcd_fd = -1;
    }

    pthread_mutex_destroy(&lcd.lock);

    if (lcd.CacheBuffer) {
        free(lcd.CacheBuffer);
        lcd.CacheBuffer = NULL;
    }

}

// 解码 JPEG 图片并存储到缓冲区
static int decode_jpeg_image(const char *path, struct jpeg_msg  * jpeg_msg_file)
{
    if (!jpeg_msg_file) {
        fprintf(stderr, "jpeg_msg_file is NULL\n");
        return -1;
    }
    struct jpeg_decompress_struct cinfo;
    struct jpeg_error_mgr jerr;
    FILE *jpeg_file = NULL;

    unsigned char *jpeg_line_buf = NULL;     //行缓冲区:用于存储从jpeg文件中解压出来的一行图像数据
    unsigned int min_h, min_w;


    //绑定默认错误处理函数
    cinfo.err = jpeg_std_error(&jerr);
 
    //打开.jpeg/.jpg图像文件
    jpeg_file = fopen(path, "r");   //只读方式打开
    if (NULL == jpeg_file) {
        perror("fopen error");
        return -1;
    }
 
    //创建JPEG解码对象
    jpeg_create_decompress(&cinfo);
    if (!cinfo.mem) {
        printf("jpeg_create_decompress failed!\n");
        fclose(jpeg_file);
        return -1;
    }
    //指定图像文件
    jpeg_stdio_src(&cinfo, jpeg_file);

    //读取图像信息
    jpeg_read_header(&cinfo, TRUE);
    printf("jpeg图像:%s的大小: %d*%d\n", path,cinfo.image_width, cinfo.image_height);

    //设置解码参数
    cinfo.out_color_space = JCS_RGB;//默认就是JCS_RGB

    //开始解码图像
    jpeg_start_decompress(&cinfo);
 
    //为缓冲区分配内存空间
    jpeg_line_buf = (unsigned char*) malloc(cinfo.output_components * cinfo.output_width);
    printf("每个像素的字节数: %d\n", cinfo.output_components); // 应该是 3
    if (jpeg_line_buf == NULL) {
        perror("malloc jpeg_line_buf error");
        fclose(jpeg_file);
        return -1;
    }
 
    //判断图像和LCD屏那个的分辨率更低
    min_w = MIN(lcd.width,cinfo.output_width);
    min_h = MIN(lcd.height,cinfo.output_height);

    //分配存储解码后图片的缓冲区
    jpeg_msg_file->buffer = malloc(min_w * min_h * lcd.bpp / 8);
    printf("分配内存的大小为%d\r\n",min_w * min_h * lcd.bpp / 8);
    if (jpeg_msg_file->buffer == NULL) {
        perror("malloc jpeg_msg_file->buffer error");
        free(jpeg_line_buf);
        fclose(jpeg_file);
        return -1;
    }
    jpeg_msg_file->height = min_h;
    jpeg_msg_file->width = min_w;

    printf("准备开始转换 min_h : %d min_w : %d \r\n",min_h, min_w);
    // 逐行读取数据并转换为RGB565
    for (unsigned int y = 0; y < min_h; y++) {
        if (jpeg_read_scanlines(&cinfo, &jpeg_line_buf, 1) != 1) {
            printf("jpeg_read_scanlines failed at line %d\n", y);
            break;
        }
        uint16_t *dst = (unsigned short *)jpeg_msg_file->buffer + y * min_w; // 目标RGB565缓冲区
        unsigned char *src = jpeg_line_buf; // RGB888数据起始地址
        
        for (unsigned int x = 0; x < min_w; x++) {
            uint8_t r = src[0];
            uint8_t g = src[1];
            uint8_t b = src[2];
            dst[x] = RGB888_to_RGB565(r, g, b); // 转换并存储
            src += 3; // 移动到下一个像素
        }
    }
    //printf("解码完成\r\n");
    jpeg_finish_decompress(&cinfo); //完成解码
    //printf("解码完成1\r\n");
    //printf("释放前 jpeg_msg_file->buffer: %p\n", jpeg_msg_file->buffer);
    jpeg_destroy_decompress(&cinfo);
    //printf("释放后 jpeg_msg_file->buffer: %p\n", jpeg_msg_file->buffer);
    printf("解码完成2\r\n");
    //关闭文件、释放内存
    fclose(jpeg_file);
    //printf("解码完成3\r\n");
    free(jpeg_line_buf);
    jpeg_line_buf = NULL;
    printf("解码完成4\r\n");
    return 0;
}

// 显示解码后的图片数据 ----放到lcd.cacheBuffer的指定位置就行
// 对于 第一张图片的显示位置: (0,400) (200,480)
// 对于 第二张图片的显示位置: (200,400) (400,480)
// 对于 对于第三张图片的显示位置: (400,400) (600,480)
static void store_image_buffer(struct jpeg_msg  * jpeg_msg_file,unsigned char index)
{
    // 计算可显示区域
    unsigned int display_width = MIN(jpeg_msg_file->width, PIC_WIDTH);
    unsigned int display_height = MIN(jpeg_msg_file->height, PIC_HEIGHT);

    unsigned int start_x = -1;
    unsigned int start_y = -1;
    switch(index) {
        case 0:
            start_x = PIC1_START_X;
            start_y = PIC_START_Y;
            break;
        case 1:
            start_x = PIC2_START_X;
            start_y = PIC_START_Y;
            break;
        case 2:
            start_x = PIC3_START_X;
            start_y = PIC_START_Y;
            break;
        default:
            fprintf(stderr, "error pic index\n");
            return;
    };
    printf("start_x:%d, start_y:%d",start_x,start_y);
    // 计算起始地址
    unsigned char  *lcd_ptr = lcd.CacheBuffer + start_y * (lcd.width * lcd.bpp / 8) + start_x * (lcd.bpp / 8);
    unsigned char *img_ptr = jpeg_msg_file->buffer;

    // 遍历 JPEG 缓冲区，逐行拷贝到 LCD
    for (unsigned int row = 0; row < display_height; row++) {
        memcpy(lcd_ptr, img_ptr, display_width * (lcd.bpp / 8));    // 复制一行数据
        lcd_ptr += lcd.width * (lcd.bpp / 8);                        // 移动到下一行 LCD
        img_ptr += jpeg_msg_file->width * (lcd.bpp / 8);            // 移动到 JPEG 缓冲区的下一行(转RGB565了)
    }
    free(jpeg_msg_file->buffer);
    jpeg_msg_file->buffer = NULL;
    return;
}

void lcd_put_pixel(int x, int y, unsigned int color)
{
	unsigned char *pen_8 = lcd.CacheBuffer + y * (lcd.width * lcd.bpp / 8) + x * lcd.bpp / 8;
	unsigned short *pen_16;	
	unsigned int *pen_32;	

	unsigned int red, green, blue;	

	pen_16 = (unsigned short *)pen_8;
	pen_32 = (unsigned int *)pen_8;

	switch (lcd.bpp)
	{
		case 8:
		{
			*pen_8 = color;
			break;
		}
		case 16:
		{
			/* 565 */
			red   = (color >> 16) & 0xff;
			green = (color >> 8) & 0xff;
			blue  = (color >> 0) & 0xff;
			color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
			*pen_16 = color;
			break;
		}
		case 32:
		{
			*pen_32 = color;
			break;
		}
		default:
		{
			fprintf(stderr,"can't surport %dbpp\n", lcd.bpp);
			break;
		}
	}
}

// 大小是 8 * 16 (实际占用 8 * bpp * 16)
void lcd_put_ascii(int x, int y, unsigned char c)
{
    //printf("显示位置:%d %d\r\n",x,y);
	unsigned char *dots = (unsigned char *)&fontdata_8x16[c*16];
	int i, b;
	unsigned char byte;

	for (i = 0; i < 16; i++)
	{
		byte = dots[i];
		for (b = 7; b >= 0; b--)
		{
			if (byte & (1<<b))
			{
				/* show */
				lcd_put_pixel(x+7-b, y+i, 0);       /* 白 */
			}
			else
			{
				/* hide */
				lcd_put_pixel(x+7-b, y+i, 0xffffff); /* 黑 */
			}
		}
	}
}
// 把文本写进来 
static int store_text(const char * text,unsigned int length)
{

    // 每个字的大小是 8 * 16  (实际是8 * bpp * 16)
    int chars_per_line = (lcd.width - TEXT_START_X) / 8; // 一行就显示这么多字 一个字需要8个x
    int num_lines = (length + chars_per_line - 1) / chars_per_line; // 需要显示的行数
    //printf("每行显示的字数:%d,显示几行%d\r\n",chars_per_line,num_lines);
    unsigned int count = 0;
    for (int line = 0; line < num_lines; line++) {
        int show_char_num = (length < chars_per_line) ? length : chars_per_line;
        for (int i = 0; i < show_char_num; i++) {
            lcd_put_ascii(TEXT_START_X + i * 8, lcd.current_text_row, text[count++]);
        }
        lcd.current_text_row += 16;
        if (lcd.current_text_row >= lcd.height) {
            lcd.current_text_row = 0;
        }
        length -= show_char_num;
    }
    //printf("更新完成\r\n");
    return 0;
}

// 由定时器决定 把CacheBuffer刷新到screen_base
static int flush_to_memory()
{
    //printf("最近一块使用的current_frame%d",lcd.current_frame);
    // 刷新进 screen_base
    if (!lcd.CacheBuffer || lcd.CacheBuffer == NULL) {
        printf("lcd.CacheBuffer is NULL in flush_to_memory()\n");
        return -1;
    }
    memcpy(lcd.screen_base, lcd.CacheBuffer, lcd.screen_size);
    return 0;
}

static void timer_handler(union sigval sv) {
    static int text_count = 0;
    const char* msg= "LCS666";
    struct msg message;
    message.type = LCD_MSG_TYPE;
    message.detail_msg.lcd_msg = LCD_FLUSH_READY;
    if (msgsnd(lcd.msg_fd, &message, sizeof(message.detail_msg), IPC_NOWAIT) == -1) {
        perror("msgsnd LCD_FLUSH_READY error");
    }
    text_count++;
    if(text_count % 20)
    {
        message.type = LCD_MSG_TYPE;
        message.detail_msg.lcd_msg = LCD_TEXT_CHANGE;
        snprintf(message.detail_msg.msg_buffer,MSG_MAX_LENGTH,"%s",msg);
        message.detail_msg.msg_length = strlen(msg);
        if (msgsnd(lcd.msg_fd, &message, sizeof(message.detail_msg), IPC_NOWAIT) == -1) {
            perror("msgsnd LCD_TEXT_CHANGE error");
        }
    }


}

void* lcd_thread(void * pvParameter)
{
    printf("LCD现成创建成功\r\n");
    // 外设初始化在主线程完成
    // 这里生成一个alrm信号 每次刷新完
    // 消息队列不断接收消息然后进行更新 switch-case
    lcd.msg_fd  = *(int*) pvParameter;  //全局变量 都要用到

    // 把图片解压缩放入指定位置
    decode_jpeg_image(PIC1_PATH, &jpeg_fileA);
    store_image_buffer(&jpeg_fileA,0);
    decode_jpeg_image(PIC2_PATH, &jpeg_fileB);
    store_image_buffer(&jpeg_fileB,1);
    decode_jpeg_image(PIC3_PATH, &jpeg_fileC);
    store_image_buffer(&jpeg_fileC,2);

    struct sigevent sev;
    // 配置定时器
    sev.sigev_notify = SIGEV_THREAD;
    sev.sigev_notify_function = timer_handler;
    sev.sigev_value.sival_ptr = NULL; // 传递线程 ID
    sev.sigev_notify_attributes = NULL;

    // 创建定时器
    if (timer_create(CLOCK_MONOTONIC, &sev, &lcd.timerid) != 0) {
        perror("timer_create 失败");
        pthread_exit(NULL);
    }
    // 设置定时器：每30毫秒触发一次
    lcd.lcd_timer.it_value.tv_sec = 0;                // 初次触发时间为 0 秒
    lcd.lcd_timer.it_value.tv_nsec = 30000000;        // 初次触发时间为 30 毫秒 (30,000,000 纳秒)
    lcd.lcd_timer.it_interval.tv_sec = 0;             // 周期为 0 秒
    lcd.lcd_timer.it_interval.tv_nsec = 30000000;     // 每次周期触发间隔 30 毫秒

    if (timer_settime(lcd.timerid, 0, &lcd.lcd_timer, NULL) != 0) {
        perror("lcd timer_settime 失败");
        pthread_exit(NULL);
    }
     // 消息队列不断接收消息然后进行更新
     printf("定时器准备接收消息");
     while (1) {
        // 接收消息队列中的消息
        struct msg message;
        if (msgrcv(lcd.msg_fd, &message, sizeof(message.detail_msg), LCD_MSG_TYPE, 0) == -1) {
            perror("msgrcv error");
            continue;
        }
  
        switch (message.detail_msg.lcd_msg) {
            case LCD_FLUSH_READY:
                // 处理LED消息
                //printf("准备刷新到内存\r\n");
                pthread_mutex_lock(&lcd.lock);
                flush_to_memory();
                pthread_mutex_unlock(&lcd.lock);
                break;
            case LCD_TEXT_CHANGE:
                //printf("准备更新文本\r\n");
                pthread_mutex_lock(&lcd.lock);
                store_text(message.detail_msg.msg_buffer, message.detail_msg.msg_length);
                pthread_mutex_unlock(&lcd.lock);
                break;
            default:
                fprintf(stderr, "Unknown message type\n");
                break;
        }
    }
    lcd_exit();
    pthread_exit(NULL);
}
static inline uint16_t yuyv_to_rgb565(unsigned char y, unsigned char u, unsigned char v) {
    int r, g, b;
    int c = y - 16;
    int d = u - 128;
    int e = v - 128;

    // BT.601, 625 line conversion formula
    r = (298 * c + 409 * e + 128) >> 8;
    g = (298 * c - 100 * d - 208 * e + 128) >> 8;
    b = (298 * c + 516 * d + 128) >> 8;

    // Clip rgb values to be in [0..255] range
    r = r > 255 ? 255 : (r < 0 ? 0 : r);
    g = g > 255 ? 255 : (g < 0 ? 0 : g);
    b = b > 255 ? 255 : (b < 0 ? 0 : b);

    // Convert rgb to rgb565 format
    return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
}

static void convert_yuyv_to_rgb565_inplace(unsigned char* buffer, int size) {
    uint16_t* rgb565_buffer = (uint16_t*)buffer; 

    for(int i = 0; i < size; i += 4) { 
        // First pixel
        uint16_t rgb565_0 = yuyv_to_rgb565(buffer[i], buffer[i+1], buffer[i+3]);
        rgb565_buffer[i/2] = rgb565_0;

        
        if(i+4 <= size) {
            uint16_t rgb565_1 = yuyv_to_rgb565(buffer[i+2], buffer[i+1], buffer[i+3]);
            rgb565_buffer[i/2 + 1] = rgb565_1;
        }
    }
}
// 把图像刷新到指定位置
static void flush_pic_to_cache(void * buffer, struct carama_msg recv_msg) {
    int min_w = MIN(recv_msg.width, CARAMA_MAX_X); // 规定的显示区域的大小
    int min_height = MIN(recv_msg.height, CARAMA_MAX_Y);
    unsigned char *lcd_ptr = lcd.CacheBuffer; // LCD缓存区指针
    unsigned char *image_ptr = buffer; // 输入图像数据指针

    // 计算一行像素占用的字节数，假设lcd.bpp是每个像素的比特数
    int lcd_bytes_per_row = (lcd.width * lcd.bpp / 8);

    for(int i = 0; i < CARAMA_MAX_Y; i++) {
        unsigned char *current_lcd_row = lcd_ptr + i * lcd_bytes_per_row;

        if(i < min_height) {
            // 对于有图像数据的行，先复制图像数据到LCD缓存
            memcpy(current_lcd_row, image_ptr + (i * recv_msg.width * 2), min_w * 2); // YUYV格式每个像素占2字节
            
            // 然后填充剩余部分为白色（如果有的话）
            if(min_w < CARAMA_MAX_X) {
                memset(current_lcd_row + (min_w * 2), 0xFF, (CARAMA_MAX_X - min_w) * 2); // 剩余部分填充白色
            }
        } else {
            // 对于没有图像数据的行，全部填充为白色
            memset(current_lcd_row, 0xFF, lcd_bytes_per_row);
        }
    }
}
void * convert_thread(void * pvParameter)
{
    printf("convert thread创建成功\r\n");
    cal_buffer = malloc(CARAMA_MAX_X * CARAMA_MAX_Y * YUYV_FORMAT);   // 最大的buffer了
    struct carama_msg recv_msg;
    static unsigned  readIndex = 0;
    if(cal_buffer == NULL)
    {
        printf("缓存分配失败\r\n");
        pthread_exit(NULL);
    }
    while(1) {
        kfifo_read(global_carama_fifo,cal_buffer,CARAMA_MAX_WIDTH * CARAMA_MAX_HEIGHT * 2,
            &recv_msg,&readIndex);
        //printf("终于读到了readIndex: %d,理想:%ld,实际:%d\r\n",readIndex,CARAMA_MAX_WIDTH * CARAMA_MAX_HEIGHT * 2,recv_msg.real_size);
        // 读到数据了就做个转换
        convert_yuyv_to_rgb565_inplace(cal_buffer, recv_msg.real_size);
        //printf("处理完一帧数据了\r\n");
        pthread_mutex_lock(&lcd.lock);
        flush_pic_to_cache(cal_buffer,recv_msg);
        pthread_mutex_unlock(&lcd.lock);
    }
    free(cal_buffer);
    cal_buffer = NULL;
}
// 还需要一个线程 yuyv的数据转为rgb565才能显示

// int main()
// {
//     // 创建消息队列
//     msg_key = msgget(12345, IPC_CREAT | 0666);
//     if (msg_key == -1) {
//         perror("msgget error");
//         lcd_exit();
//         return -1;
//     }
//     // 放入lcd的CacheBuffer中
//     pthread_t lcd_tid;
//     if (pthread_create(&lcd_tid, NULL, lcd_thread, &msg_key) != 0) {
//         perror("pthread_create error");
//         msgctl(msg_key, IPC_RMID, NULL); // 删除消息队列
//         lcd_exit();
//         return -1;
//     }
//     //等待线程结束
//     pthread_join(lcd_tid, NULL);
//     // 删除消息队列
//     msgctl(msg_key, IPC_RMID, NULL);
//     // 释放资源
//     lcd_exit();
//     return 0;
// }
 